<?php
namespace WeChatPay;

use WeChatPay\Request\WeChatPayTradeAppPayRequest;
use WeChatPay\Request\WeChatPayTradeDownloadBillRequest;
use WeChatPay\Request\WeChatPayTradeH5PayRequest;
use WeChatPay\Request\WeChatPayTradeJsApiPayRequest;
use WeChatPay\Request\WeChatPayTradeClientOpenWxSignRequest;
use WeChatPay\Request\WeChatPayTradeNativePayRequest;
use WeChatPay\Request\WeChatPayTradeNotifyVerifyRequest;
use WeChatPay\Request\WeChatPayTradeOutRefundNoRefundQueryRequest;
use WeChatPay\Request\WeChatPayTradeOutTradeNoCloseRequest;
use WeChatPay\Request\WeChatPayTradeOutTradeNoQueryRequest;
use WeChatPay\Request\WeChatPayTradeOutTradeNoRefundRequest;
use WeChatPay\Request\WeChatPayTradeServiceSignRequest;
use WeChatPay\Request\WeChatPayTradeTradeBillRequest;
use WeChatPay\Request\WeChatPayTradeTransactionIdQueryRequest;
use WeChatPay\Request\WeChatPayTradeTransactionIdRefundRequest;

/**
 * 微信支付
 * Class WeChatSdk
 * @package WeChatPay
 */
class WeChatSdk
{
    //微信支付配置
    private $config;

    /**
     * 微信支付服务控制
     * @var WeChatTradeService
     */
    protected $weChatPayService = null;

    /**
     * WeChatSdk constructor.
     * @param array $config
     */
    public function __construct($config=[])
    {
        if (empty($config)) {
            $this->config = require_once "config.php";
        }else{
            $this->config=$config;
        }
        $this->weChatPayService = new WeChatTradeService($this->config);
    }

    /**
     * 设置当前SDK的配置
     * @param array $config
     * @return $this
     */
    public function setConfig(array $config) {
        $this->config=$config;
        $this->weChatPayService->reset()->setConfig($this->config);
        return $this;
    }

    /**
     * 扫码支付
     * @param string $outTradeNo 商户订单号
     * @param string $description 商品描述
     * @param array $amount 订单金额 ['total'=>1, 'currency'=>'CNY']
     * @param array $otherParams 其余参数 key为参数名称,value为参数值。剩余参数列表['time_expire','attach','goods_tag','detail','scene_info','settle_info']
     * @return array
     * @throws \Exception
     */
    public function nativePay(string $outTradeNo, string $description, array $amount, array $otherParams = []) {
        try {
            $request = new WeChatPayTradeNativePayRequest();
            $request->setDescription($description);
            $request->setOutTradeNo($outTradeNo);
            $request->setAmount($amount);
            $request->setManyParams($otherParams);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * JSAPI支付 (小程序、公众号支付)
     * @param string $outTradeNo 商户订单号
     * @param array $payer 支付者信息 ['openid]=>'']
     * @param string $description 商品描述
     * @param array $amount 订单金额 ['total'=>1, 'currency'=>'CNY']
     * @param array $otherParams 其余参数 key为参数名称,value为参数值。剩余参数列表['time_expire','attach','goods_tag','detail','scene_info','settle_info']
     * @return array
     * @throws \Exception
     */
    public function jsapiPay(string $outTradeNo, array $payer, string $description, array $amount, array $otherParams = []) {
        try {
            $request = new WeChatPayTradeJsApiPayRequest();
            $request->setPayer($payer);
            $request->setDescription($description);
            $request->setOutTradeNo($outTradeNo);
            $request->setAmount($amount);
            $request->setManyParams($otherParams);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * H5支付(WAP支付)
     * @param string $outTradeNo 商户订单号
     * @param string $description 商品描述
     * @param array $amount 订单金额 ['total'=>1, 'currency'=>'CNY']
     * @param array $sceneInfo 场景信息 ['h5_info'=>[], 'store_info'=>[]]
     * @param array $otherParams 其余参数 key为参数名称,value为参数值。剩余参数列表['time_expire','attach','goods_tag','detail','scene_info','settle_info']
     * @return array
     * @throws \Exception
     */
    public function h5Pay(string $outTradeNo, string $description, array $amount, array $sceneInfo, array $otherParams = []) {
        try {
            $request = new WeChatPayTradeH5PayRequest();
            $request->setDescription($description);
            $request->setOutTradeNo($outTradeNo);
            $request->setAmount($amount);
            $request->setSceneInfo($sceneInfo);
            $request->setManyParams($otherParams);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * APP支付
     * @param string $outTradeNo 商户订单号
     * @param string $description 商品描述
     * @param array $amount 订单金额 ['total'=>1, 'currency'=>'CNY']
     * @param array $otherParams 其余参数 key为参数名称,value为参数值。剩余参数列表['time_expire','attach','goods_tag','detail','scene_info','settle_info']
     * @return array
     * @throws \Exception
     */
    public function appPay(string $outTradeNo, string $description, array $amount, array $otherParams = []) {
        try {
            $request = new WeChatPayTradeAppPayRequest();
            $request->setDescription($description);
            $request->setOutTradeNo($outTradeNo);
            $request->setAmount($amount);
            $request->setManyParams($otherParams);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 微信支付订单号查询
     * @param string $transactionId 微信支付订单号
     * @return array
     */
    public function transactionIdQuery(string $transactionId) {
        try {
            $request = new WeChatPayTradeTransactionIdQueryRequest();
            $request->setTransactionId($transactionId);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 商户订单号查询
     * @param string $outTradeNo 商户订单号
     * @return array
     */
    public function outTradeNoQuery(string $outTradeNo) {
        try {
            $request = new WeChatPayTradeOutTradeNoQueryRequest();
            $request->setOutTradeNo($outTradeNo);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 关闭订单API
     * @param string $outTradeNo 商户订单号
     * @return array
     */
    public function outTradeNoClose(string $outTradeNo) {
        try {
            $request = new WeChatPayTradeOutTradeNoCloseRequest();
            $request->setOutTradeNo($outTradeNo);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 申请退款API (微信支付订单号进行退款)
     * @param string $transactionId 微信支付订单号
     * @param string $outRefundNo 商户退款单号
     * @param array $amount 金额信息 ['refund'=>1, 'from'=>[], 'total'=>1, 'currency'=>'CNY']
     * @param array $otherParams 其余参数 key为参数名称,value为参数值。剩余参数列表['reason','funds_account','goods_detail']
     * @return array
     */
    public function transactionIdRefund(string $transactionId, string $outRefundNo, array $amount, array $otherParams = []) {
        try {
            $request = new WeChatPayTradeTransactionIdRefundRequest();
            $request->setTransactionId($transactionId);
            $request->setOutRefundNo($outRefundNo);
            $request->setAmount($amount);
            $request->setManyParams($otherParams);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 申请退款API (商户订单号进行退款)
     * @param string $outTradeNo 商户订单号
     * @param string $outRefundNo 商户退款单号
     * @param array $amount 金额信息 ['refund'=>1, 'from'=>[], 'total'=>1, 'currency'=>'CNY']
     * @param array $otherParams 其余参数 key为参数名称,value为参数值。剩余参数列表['reason','funds_account','goods_detail']
     * @return array
     */
    public function outTradeNoRefund(string $outTradeNo, string $outRefundNo, array $amount, array $otherParams = []) {
        try {
            $request = new WeChatPayTradeOutTradeNoRefundRequest();
            $request->setOutTradeNo($outTradeNo);
            $request->setOutRefundNo($outRefundNo);
            $request->setAmount($amount);
            $request->setManyParams($otherParams);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 查询单笔退款API
     * @param string $outRefundNo 商户退款单号
     * @return array
     */
    public function outRefundNoRefundQuery(string $outRefundNo) {
        try {
            $request = new WeChatPayTradeOutRefundNoRefundQueryRequest();
            $request->setOutRefundNo($outRefundNo);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 申请交易账单API
     * @param string $billDate 账单日期
     * @return array
     */
    public function tradeBill(string $billDate) {
        try {
            $request = new WeChatPayTradeTradeBillRequest();
            $request->setBillDate($billDate);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 申请资金账单API
     * @param string $billDate 账单日期
     * @return array
     */
    public function fundFlowBill(string $billDate) {
        try {
            $request = new WeChatPayTradeTradeBillRequest();
            $request->setBillDate($billDate);
            return $this->weChatPayService->getResponse($request);
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 下载账单
     * @param string $apiUrl 账单下载地址
     * @return array
     */
    public function downloadBill(string $apiUrl) {
        try {
            $request = new WeChatPayTradeDownloadBillRequest();
            $request->setApiUrl($apiUrl);
            //移除响应签名验证中间件
            $stack = $this->weChatPayService->getHttpRequestInstance()->getDriver()->select()->getConfig('handler');
            $stack->remove('verifier');

            $responseObj = $this->weChatPayService->getResponse($request);
            $this->setConfig($this->config);
            return $responseObj;
        } catch (\Throwable $exception) {
            return FuncReturn(WX_RETURN_FAIL, $exception->getMessage());
        }
    }

    /**
     * 生成服务器端API请求签名
     * @param $url 请求地址URL
     * @param $requestType 请求方法GET/POST
     * @param $requestBody 请求体BODY内容，json字符串
     * @param string $timestamp 时间戳
     * @param string $nonce 随机字符串
     * @return array
     */
    public function serviceSign($url, $requestType, $requestBody, $timestamp = "", $nonce = "") {
        try {
            $request = new WeChatPayTradeServiceSignRequest();
            $request->setApiUri($url);
            $request->setRequestType($requestType);
            $request->setBody($requestBody);
            $request->setTimestamp($timestamp);
            $request->setNonce($nonce);
            $responseObj = $this->weChatPayService->serviceSign($request);
            return $responseObj;
        } catch (\Throwable $exception) {
            return FuncReturn($exception->getCode(), $exception->getMessage());
        }
    }

    /**
     * 创建客户端调起微信支付签名(小程序、APP支付)
     * @param $prepayId 支付参数
     * @return array
     */
    public function clientOpenWxSign($prepayId) {
        try {
            $request = new WeChatPayTradeClientOpenWxSignRequest();
            $request->setBody("prepay_id=" . $prepayId);
            $responseObj = $this->weChatPayService->clientOpenWxSign($request);
            return $responseObj;
        } catch (\Throwable $exception) {
            return FuncReturn($exception->getCode(), $exception->getMessage());
        }
    }

    /**
     * 异步回调验签
     * @param string $body 响应内容体
     * @param string $nonce 随机字符串
     * @param string $serial 平台证书序列号
     * @param string $timestamp 时间戳
     * @param string $signature 签名字符串
     * @return array
     */
    public function notifyVerify(string $body, string $nonce, string $serial, string $timestamp, string $signature) {
        try {
            $request = new WeChatPayTradeNotifyVerifyRequest();
            $request->setWeChatPayBody($body);
            $request->setWeChatPaySignature($signature);
            $request->setWeChatPayNonce($nonce);
            $request->setWeChatPaySerial($serial);
            $request->setWeChatPayTimestamp($timestamp);
            $responseObj = $this->weChatPayService->notifyVerify($request);
            return $responseObj;
        } catch (\Throwable $exception) {
            return FuncReturn($exception->getCode(), $exception->getMessage());
        }
    }
}